package com.jc.gateway.filter;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jc.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;


@Component
@Slf4j
public class AccessGatewayFilterFactory extends AbstractGatewayFilterFactory<AccessGatewayFilterFactory.Config> {

    public AccessGatewayFilterFactory() {
        super(Config.class);
        this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
    }

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    @Lazy
    private AccessUtils accessUtils;

    private final List<HttpMessageReader<?>> messageReaders;


    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {

            logger.info("****entry AccessGatewayFilterFactory****");

            MediaType contentType = exchange.getRequest().getHeaders().getContentType();
            // 针对 multipart/form-data 类型的请求进行特殊处理（文件上传）
            if (contentType != null && contentType.includes(MediaType.MULTIPART_FORM_DATA)) {
                logger.info("Skip validation for file upload request");
                HttpHeaders headers = exchange.getRequest().getHeaders();
                String secretId = headers.getFirst("secretId");
                String sign = headers.getFirst("sign");
                String timestampStr = headers.getFirst("timestamp");
                
                // 验证请求头参数完整性
                if (secretId == null || sign == null || timestampStr == null) {
                    return handleError(exchange, "Missing required headers", HttpStatus.UNAUTHORIZED);
                }
                Long timestamp;
                try {
                    timestamp = Long.parseLong(timestampStr);
                } catch (NumberFormatException e) {
                    return handleError(exchange, "Invalid timestamp format", HttpStatus.UNAUTHORIZED);
                }
                // 验证请求头中的签名信息
                Result<Void> validationResult = accessUtils.validateFromHeaders(secretId, sign, timestamp);
                if (!validationResult.getSuccess()) {
                    return handleError(exchange, validationResult.getMessage(), HttpStatus.UNAUTHORIZED);
                }
                config.setR(validationResult.getSuccess()); // 标记为验证通过
                return chain.filter(exchange.mutate().build());
            }

            // 创建 ServerRequest 对象用于读取请求体
            ServerRequest serverRequest = ServerRequest.create(exchange, this.messageReaders);

            // 处理请求体内容，进行解码和验证
            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                    .flatMap(originalBody -> {
                        String uri = exchange.getRequest().getURI().toString();
                        // 调用 accessUtils 解码请求体
                        Result<String> result = accessUtils.decode(originalBody, uri);
                        config.setR(result.getSuccess());
                        
                        if (result.getSuccess()) {
                            // 解码成功，使用解码后的内容
                            return Mono.just(result.getContent());
                        } else {
                            // 解码失败，返回错误信息
                            try {
                                String errorResponse = new ObjectMapper().writeValueAsString(result);
                                return Mono.just(errorResponse);
                            } catch (JsonProcessingException e) {
                                log.error("Error processing JSON response: " + e.getMessage(), e);
                                return Mono.just("{\"success\":false,\"message\":\"Internal server error\"}");
                            }
                        }
                    });

            // 创建 BodyInserter 用于修改请求体
            BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);

            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());

            // the new content type will be computed by bodyInserter
            // and then set in the request decorator
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            return bodyInserter.insert(outputMessage, new BodyInserterContext())
                    .then(Mono.defer(() -> {
                                if (config.getR()) {
                                    // 验证通过，创建装饰器修改请求
                                    ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                                        @Override
                                        public HttpHeaders getHeaders() {
                                            long contentLength = headers.getContentLength();
                                            HttpHeaders httpHeaders = new HttpHeaders();
                                            httpHeaders.putAll(super.getHeaders());
                                            if (contentLength > 0) {
                                                httpHeaders.setContentLength(contentLength);
                                            } else {
                                                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                                            }
                                            return httpHeaders;
                                        }

                                        @Override
                                        public Flux<DataBuffer> getBody() {
                                            return outputMessage.getBody();
                                        }
                                    };
                                    return chain.filter(exchange.mutate().request(decorator).build());
                                } else {
                                    // 验证失败，返回错误响应
                                    Flux<DataBuffer> messageBody = outputMessage.getBody();
                                    ServerHttpResponse serverHttpResponse = exchange.getResponse();
                                    serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
                                    return exchange.getResponse().writeWith(messageBody);
                                }
                            }
                    ));
        };
    }

    // 配置类，用于存储过滤器配置信息
    public static class Config {

        private Boolean addName;

        private Boolean result;

        public Boolean getR() {
            return result;
        }

        public void setR(Boolean result) {
            this.result = result;
        }

        public Boolean getAddName() {
            return addName;
        }

        public Config setConfig(Boolean addName) {
            this.addName = addName;
            return this;
        }

    }


    // 处理错误响应的私有方法
    private Mono<Void> handleError(ServerWebExchange exchange, String errorMsg, HttpStatus status) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(status);

        Result<String> errorResult = new Result<String>().error();
        errorResult.setMessage(errorMsg);
        errorResult.setCode(status.value());

        try {
            String json = new ObjectMapper().writeValueAsString(errorResult);
            DataBuffer buffer = response.bufferFactory().wrap(json.getBytes());
            response.getHeaders().add("Content-Type", "application/json");
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            log.error("Error writing error response: " + e.getMessage(), e);
            return Mono.error(e);
        }
    }
}